package product

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	jsoniter "github.com/json-iterator/go"
	"gorm.io/gen"
	"gorm.io/gen/field"
	"mall_admin_api/biz/dal"
	"mall_admin_api/biz/handler/common"
	"mall_admin_api/biz/model"
	"mall_admin_api/biz/model/admin_product_specifications"
	"mall_admin_api/biz/pack"
	"mall_admin_api/biz/util"
	"strings"
)

const splitKey = "-=-"

type ProductSpecificationsTemplate struct {
	commonInfo *common.Response
	dao        *dal.InitDAO
}

func NewProductSpecificationsTemplate(response *common.Response) *ProductSpecificationsTemplate {
	return &ProductSpecificationsTemplate{
		commonInfo: response,
		dao:        dal.NewInitDAO(response.GetBizInfo().ID),
	}
}

func (c *ProductSpecificationsTemplate) logAddMethod(ctx context.Context) context.Context {
	return ctx
}

func (c *ProductSpecificationsTemplate) GetList(ctx context.Context, req *admin_product_specifications.GetSpecificationsListRequest, page, size int) ([]*admin_product_specifications.Specifications, int64, error) {
	ctx = c.logAddMethod(ctx)
	var conditions []gen.Condition
	list, total, err := c.dao.ProductSpecificationsTemplateDAO.GetListWithTotal(ctx, &dal.ProductSpecificationsTemplateListParams{
		Condition: conditions,
		Page:      page,
		Size:      size,
	})
	if err != nil {
		hlog.CtxErrorf(ctx, "GetList err: %v", err)
		return nil, 0, err
	}
	return pack.GetProductSpecificationsList(list), total, nil
}

func (c *ProductSpecificationsTemplate) Update(ctx context.Context, req *admin_product_specifications.UpdateSpecificationsRequest) error {
	ctx = c.logAddMethod(ctx)
	var fields []field.AssignExpr
	fields = append(fields, dal.ProductSpecificationsTemplate.UpdatedBy.Value(c.commonInfo.GetAdministratorInfo().Nickname))
	if req.Name != nil {
		fields = append(fields, dal.ProductSpecificationsTemplate.Name.Value(req.GetName()))
	}
	if req.Value != nil {
		value, err := json.Marshal(&req.Value)
		if err != nil {
			hlog.CtxErrorf(ctx, "Marshal err: %v", err)
			return err
		}
		fields = append(fields, dal.ProductSpecificationsTemplate.Value.Value(string(value)))
	}
	if err := c.dao.ProductSpecificationsTemplateDAO.UpdateProductSpecificationsTemplate(ctx, []gen.Condition{dal.ProductSpecificationsTemplate.ID.Eq(req.ID)}, fields); err != nil {
		hlog.CtxErrorf(ctx, "Update err: %v", err)
		return err
	}
	return nil
}

func (c *ProductSpecificationsTemplate) Delete(ctx context.Context, req *admin_product_specifications.DeleteSpecificationsRequest) error {
	ctx = c.logAddMethod(ctx)
	if err := c.dao.ProductSpecificationsTemplateDAO.DeleteProductSpecificationsTemplate(ctx, []gen.Condition{dal.ProductSpecificationsTemplate.ID.In(req.GetIDs()...)}); err != nil {
		hlog.CtxErrorf(ctx, "Delete err: %v", err)
		return err
	}
	return nil
}

func (c *ProductSpecificationsTemplate) Create(ctx context.Context, req *admin_product_specifications.CreateSpecificationsRequest) error {
	ctx = c.logAddMethod(ctx)
	value, err := json.Marshal(&req.Value)
	if err != nil {
		hlog.CtxErrorf(ctx, "Marshal err: %v", err)
		return err
	}
	var data = &model.ProductSpecificationsTemplate{
		Name:      req.Name,
		BizID:     c.commonInfo.GetBizInfo().ID,
		Value:     string(value),
		CreatedBy: c.commonInfo.GetAdministratorInfo().Nickname,
	}
	if _, err = c.dao.ProductSpecificationsTemplateDAO.CreateProductSpecificationsTemplate(ctx, data); err != nil {
		hlog.CtxErrorf(ctx, "Create err: %v", err)
		return err
	}
	return nil
}

func (c *ProductSpecificationsTemplate) Format(ctx context.Context, req *admin_product_specifications.FormatSpecificationsRequest) (*model.FormatProductSpecification, error) {
	var headers = make([]*admin_product_specifications.FormatSpecificationsHeader, 0)
	var productSkuIDMap = make(map[string]*model.ProductItem)
	if req.ID > 0 {
		productSpecList, err := c.dao.ProductSpecDAO.GetProductSpecList(ctx, []gen.Condition{dal.ProductSpec.ProductID.Eq(req.ID)}, 0, 0)
		if err != nil {
			hlog.CtxErrorf(ctx, "GetProductSpecList err: %v", err)
			return nil, err
		}
		productSpecValueList, err := c.dao.ProductSpecValueDAO.GetProductSpecValueList(ctx, []gen.Condition{dal.ProductSpecValue.ProductID.Eq(req.ID)}, 0, 0)
		if err != nil {
			hlog.CtxErrorf(ctx, "GetProductSpecValue err: %v", err)
			return nil, err
		}
		productItemList, err := c.dao.ProductItemDAO.GetProductItemList(ctx, []gen.Condition{dal.ProductItem.ProductID.Eq(req.ID)}, 0, 0)
		if err != nil {
			hlog.CtxErrorf(ctx, "GetProductItemList err: %v", err)
			return nil, err
		}

		var productSpecMap = make(map[int64]*model.ProductSpec)
		for _, spec := range productSpecList {
			productSpecMap[spec.ID] = spec
		}

		var productSpecValueMap = make(map[int64]*model.ProductSpecValue)
		for _, value := range productSpecValueList {
			productSpecValueMap[value.ID] = value
		}

		productSkuIDMap = make(map[string]*model.ProductItem)
		for _, item := range productItemList {
			var specNameValueArr = make([]string, 0)
			specValueArr := strings.Split(item.SkuValue, ":")
			for _, key := range specValueArr {
				arr := strings.Split(key, "-")
				if len(arr) == 0 {
					continue
				}
				if arr[0] == "v" {
					spec, ok := productSpecValueMap[util.String2Int64(arr[1])]
					if ok {
						specNameValueArr = append(specNameValueArr, spec.Name)
					}
				}
			}
			productSkuIDMap[util.ArrayStringToMD5(specNameValueArr)] = item
		}
	}
	number := 1
	var keys []string
	for key, attr := range req.Attrs {
		headers = append(headers, &admin_product_specifications.FormatSpecificationsHeader{
			Title:    attr.Value,
			Align:    "center",
			Slot:     fmt.Sprintf("value%d", number),
			Key:      fmt.Sprintf("value%d", number),
			MinWidth: 130,
		})
		number++
		if key == 0 {
			keys = append(keys, attr.Detail...)
			continue
		}
		newKeys := keys
		keys = []string{}
		for _, k := range newKeys {
			for _, d := range attr.Detail {
				keys = append(keys, fmt.Sprintf("%s%s%s", k, splitKey, d))
			}
		}
	}

	value := make([]map[string]interface{}, 0)
	for _, key := range keys {
		keyArr := strings.Split(key, splitKey)
		var valueData = make(map[string]interface{})
		valueData["key"] = util.ArrayStringToMD5(keyArr)

		for i, v := range keyArr {
			valueData[fmt.Sprintf("value%d", i+1)] = v
		}

		productAttr, paOk := productSkuIDMap[valueData["key"].(string)]
		if !paOk {
			productAttr = &model.ProductItem{}
		}
		for k, v := range pack.DefaultValue() {
			switch k {
			case pack.PriceColumn:
				v = productAttr.SellPrice
			case pack.CostPriceColumn:
				v = productAttr.CostPrice
			case pack.MarketPriceColumn:
				v = productAttr.MarketPrice
			case pack.StockColumn:
				v = productAttr.StoreNums
			case pack.WeightColumn:
				v = productAttr.Weight
			case pack.PicColumn:
				v = pack.GetImage(productAttr.ThumbImage)
			}

			valueData[k] = v
		}
		value = append(value, valueData)
	}

	headers = append(headers, pack.DefaultHeaders()...)
	return &model.FormatProductSpecification{
		Header: headers,
		Value:  value,
		Attrs:  req.Attrs,
	}, nil
}

func (c *ProductSpecificationsTemplate) FormatBak(ctx context.Context, req *admin_product_specifications.FormatSpecificationsRequest) (*model.FormatProductSpecification, error) {
	var headers = make([]*admin_product_specifications.FormatSpecificationsHeader, 0)
	var attrItemMap = make(map[string]map[string]interface{})
	if req.ID > 0 {
		productInfo, err := c.dao.ProductDAO.GetProductInfoByID(ctx, req.ID)
		if err != nil {
			hlog.CtxErrorf(ctx, "Get err: %v", err)
			return nil, err
		}
		var attrs = make([]map[string]interface{}, 0)
		if err = jsoniter.UnmarshalFromString(productInfo.Specifications, &attrs); err != nil {
			hlog.CtxErrorf(ctx, "Unmarshal err: %v", err)
			return nil, err
		}
		for _, attr := range attrs {
			if key, ok := attr["key"]; ok {
				attrItemMap[key.(string)] = attr
			}
		}
	}
	number := 1
	var keys []string
	for key, attr := range req.Attrs {
		headers = append(headers, &admin_product_specifications.FormatSpecificationsHeader{
			Title:    attr.Value,
			Align:    "center",
			Slot:     fmt.Sprintf("value%d", number),
			Key:      fmt.Sprintf("value%d", number),
			MinWidth: 130,
		})
		number++
		if key == 0 {
			keys = append(keys, attr.Detail...)
			continue
		}
		newKeys := keys
		keys = []string{}
		for _, k := range newKeys {
			for _, d := range attr.Detail {
				keys = append(keys, fmt.Sprintf("%s-%s", k, d))
			}
		}
	}

	value := make([]map[string]interface{}, 0)
	for _, key := range keys {

		keyArr := strings.Split(key, "-")

		var valueData = make(map[string]interface{})
		valueData["key"] = util.ArrayStringToMD5(keyArr)

		for i, v := range keyArr {
			valueData[fmt.Sprintf("value%d", i+1)] = v
		}

		productAttr, paOk := attrItemMap[valueData["key"].(string)]
		if !paOk {
			productAttr = make(map[string]interface{})
		}

		for k, v := range pack.DefaultValue() {
			if productAttrValue, pavOK := productAttr[k]; pavOK {
				if k == pack.PicColumn {
					productAttrValue = pack.GetImage(productAttrValue.(string))
				}
				v = productAttrValue
			}
			valueData[k] = v
		}
		value = append(value, valueData)
	}

	headers = append(headers, pack.DefaultHeaders()...)
	return &model.FormatProductSpecification{
		Header: headers,
		Value:  value,
		Attrs:  req.Attrs,
	}, nil
}
